package no.nordicsemi.android.nrfmesh.provisioners;

import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;


import no.nordicsemi.android.mesh.MeshNetwork;
import no.nordicsemi.android.mesh.Provisioner;
import no.nordicsemi.android.mesh.provisionerstates.ProvisioningCapabilities;
import no.nordicsemi.android.mesh.provisionerstates.ProvisioningFailedState;
import no.nordicsemi.android.mesh.provisionerstates.UnprovisionedMeshNode;
import no.nordicsemi.android.mesh.utils.AuthenticationOOBMethods;
import no.nordicsemi.android.mesh.utils.InputOOBAction;
import no.nordicsemi.android.mesh.utils.OutputOOBAction;
import no.nordicsemi.android.mesh.utils.StaticOOBType;
import no.nordicsemi.android.nrfmesh.adapter.ExtendedBluetoothDevice;
import no.nordicsemi.android.nrfmesh.utils.Utils;
import no.nordicsemi.android.nrfmesh.viewmodels.ProvisionerProgress;
import no.nordicsemi.android.nrfmesh.viewmodels.ProvisioningViewModel;
import no.nordicsemi.android.nrfmesh.utils.ProvisionerStates;

import java.lang.reflect.Method;

public class MeshHeadlessProvisioner {
    // === 配置选项 ===
    public enum PreferredOob { NO_OOB, STATIC, INPUT, OUTPUT }
    public enum OobPolicy { FAIL_IF_OOB_NEEDED, ASK_UI_IF_OOB_NEEDED }


    public static class Options {
        public boolean autoProvision = true;
        public PreferredOob preferredOob = PreferredOob.NO_OOB;
        @Nullable public String nodeName;
        @Nullable public String staticOob; // 若选 STATIC，可直接填入
        @Nullable public byte[] publicKey; // 若设备支持公钥 OOB，可提前注入
        public int attentionSeconds = 0; // 需要的话可在 identify 时使用（此处保留）
        public OobPolicy oobPolicy = OobPolicy.ASK_UI_IF_OOB_NEEDED;


        public static Options noOob(String nodeName) {
            Options o = new Options();
            o.preferredOob = PreferredOob.NO_OOB;
            o.nodeName = nodeName;
            return o;
        }
    }

    // === 回调给外层（比如 ScannerActivity）用来更新界面 ===
    public interface Callbacks {
        void onProvisionStart(ExtendedBluetoothDevice device);
        void onProgressStateChanged(ProvisionerStates state);
        void onRequireOobInput(@NonNull OobRequest request); // 需要用户输入 OOB（PIN、按键次数等）
        void onFailed(@NonNull String reason);
        void onCompleted(@NonNull Intent resultIntent); // 与原 Activity setResult 保持一致
        void onLog(@NonNull String line);
    }

    public static class OobRequest {
        public final boolean isStatic;
        public final boolean isInput;
        public final boolean isOutput;
        @Nullable public final InputOOBAction[] inputActions;
        @Nullable public final OutputOOBAction[] outputActions;
        public final int inputSize;
        public final int outputSize;
        public OobRequest(boolean isStatic, boolean isInput, boolean isOutput,
                          @Nullable InputOOBAction[] inputActions,
                          @Nullable OutputOOBAction[] outputActions,
                          int inputSize, int outputSize) {
            this.isStatic = isStatic; this.isInput = isInput; this.isOutput = isOutput;
            this.inputActions = inputActions; this.outputActions = outputActions;
            this.inputSize = inputSize; this.outputSize = outputSize;
        }
    }

    private final AppCompatActivity activity;
    private final LifecycleOwner owner;
    private final Callbacks ui;
    private ProvisioningViewModel vm;


    private boolean autoIdentifyStarted = false;
    private boolean autoProvisionStarted = false;
    private boolean isRunning = false;
    private boolean wasEverConnected = false;


    private ExtendedBluetoothDevice targetDevice;
    private Options options;
    private boolean preDisconnecting;
    private boolean connectIssued;
    private final Handler mDelayHandler = new Handler(Looper.getMainLooper());
    private static final long TIMEOUT_MS = 50000L;
    private final Runnable mTimeoutRunnable = new Runnable() {
        @Override public void run() {
            if (!isRunning) return;
            fail("配网超时");
        }
    };


    public MeshHeadlessProvisioner(@NonNull AppCompatActivity activity,
                                   @NonNull LifecycleOwner owner,
                                   @NonNull Callbacks callbacks) {
        this.activity = activity;
        this.owner = owner;
        this.ui = callbacks;
        this.vm = new ViewModelProvider(activity).get(ProvisioningViewModel.class);
    }

    public boolean isRunning() { return isRunning; }

    public void start(@NonNull ExtendedBluetoothDevice device, @NonNull Options options) {
        // 若正在跑一个任务，简单拒绝或先 cancel 后再启动（这里直接拒绝）
        if (isRunning) { ui.onLog("Provision already running"); return; }

        this.targetDevice = device;
        this.options = options;
        this.isRunning = true;
        this.autoIdentifyStarted = false;
        this.autoProvisionStarted = false;
        this.wasEverConnected = false;
        this.preDisconnecting = false;
        this.connectIssued = false;

        ui.onProvisionStart(device);

        // 提前兜底网络 & 本地 Provisioner，避免 NPE
        ensureNetworkAndProvisionerReady();

        final MeshNetwork net = vm.getNetworkLiveData().getMeshNetwork();
        if (net != null && vm.getNetworkLiveData().getSelectedAppKey() == null && !net.getAppKeys().isEmpty()) {
            vm.getNetworkLiveData().setSelectedAppKey(net.getAppKeys().get(0));
        }

        vm.getNrfMeshRepository().setAutoReconnectEnabled(false);
        vm.getNrfMeshRepository().setPreferProxyAnyNode(false);

        final boolean alreadyConnected = vm.getBleMeshManager().isConnected() || Boolean.TRUE.equals(vm.isConnected().getValue());
        if (alreadyConnected) {
            preDisconnecting = true;
            vm.disconnect();
            mDelayHandler.postDelayed(() -> {
                if (isRunning && preDisconnecting && !connectIssued) {
                    preDisconnecting = false;
                    vm.connect(activity, targetDevice, false);
                    connectIssued = true;
                }
            }, 1500);
        } else {
            vm.connect(activity, device, false);
            connectIssued = true;
        }

        mDelayHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);

        // 观察连接/设备状态
        registerObservers();
    }

    public void cancel() {
        try { vm.getUnprovisionedMeshNode().removeObservers(owner); } catch (Exception ignored) {}
        try { vm.disconnect(); } catch (Exception ignored) {}
        try { vm.getNrfMeshRepository().setAutoReconnectEnabled(true); } catch (Exception ignored) {}
        try { vm.getNrfMeshRepository().setPreferProxyAnyNode(true); } catch (Exception ignored) {}
        try { mDelayHandler.removeCallbacksAndMessages(null); } catch (Exception ignored) {}
        isRunning = false;
    }

    public void submitPin(@NonNull String pin) {
        vm.getMeshManagerApi().setProvisioningAuthentication(pin);
    }

    // === 内部：注册 LiveData 观察逻辑 ===
    private void registerObservers() {
        vm.isConnected().observe(owner, connected -> {
            if (connected == null) return;
            if (connected) { wasEverConnected = true; return; }
            if (!connected) {
                if (preDisconnecting && !connectIssued) {
                    preDisconnecting = false;
                    vm.connect(activity, targetDevice, false);
                    connectIssued = true;
                    return;
                }
                if (wasEverConnected && !vm.isProvisioningComplete()) {
                    fail("Disconnected before completion");
                }
            }
        });

        vm.isDeviceReady().observe(owner, ready -> {
            if (vm.getBleMeshManager().isDeviceReady()) {
                if (vm.isProvisioningComplete()) {
                    setupProvisionerStateObservers();
                    return;
                }


                if (options.autoProvision && !autoIdentifyStarted && !vm.isProvisioningComplete()) {
                    autoIdentifyStarted = true;
                    ensureNetworkAndProvisionerReady();
                    // 把期望的节点名暂存
                    if (options.nodeName != null) targetDevice.setName(options.nodeName);
                    // Invite / Identify（等待 Capabilities 回来）
                    vm.getNrfMeshRepository().identifyNode(targetDevice);
                }
            }
        });


        vm.isReconnecting().observe(owner, isReconnecting -> {
            if (isReconnecting == null) return;
            if (isReconnecting) {
                try { vm.getUnprovisionedMeshNode().removeObservers(owner); } catch (Exception ignored) {}
            } else {
                // 重连结束，不再作为完成触发点，等待具体配置状态事件
            }
        });

        vm.getUnprovisionedMeshNode().observe(owner, node -> {
            if (node == null) return;
            final ProvisioningCapabilities caps = node.getProvisioningCapabilities();
            if (caps == null) return;


            ensureNetworkAndProvisionerReady();
            final MeshNetwork net = vm.getNetworkLiveData().getMeshNetwork();
            if (net == null) return;


            try {
                final int eleCount = caps.getNumberOfElements();
                final Provisioner prov = net.getSelectedProvisioner();
                final int unicast = net.nextAvailableUnicastAddress(eleCount, prov);
                net.assignUnicastAddress(unicast);


                // 自动 OOB 选择 & 启动（仅一次）
                if (options.autoProvision && !autoProvisionStarted) {
                    autoProvisionStarted = true;


                    if (caps.isPublicKeyOobSupported() && options.publicKey != null) {
                        onPublicKeyAdded(options.publicKey);
                        return;
                    }


                    final boolean hasNoOob = caps.getAvailableOOBTypes()
                            .contains(AuthenticationOOBMethods.NO_OOB_AUTHENTICATION);


                    switch (options.preferredOob) {
                        case NO_OOB:
                            if (hasNoOob) { onNoOOBSelected(); return; }
                            break;
                        case STATIC:
                            if (caps.isStaticOOBInformationAvailable()) { startProvisionWithStaticOob(); return; }
                            break;
                        case INPUT:
                        case OUTPUT:
                            // 下方统一处理
                            break;
                    }

                    // 兜底策略
                    if (hasNoOob) {
                        onNoOOBSelected();
                    } else if (options.oobPolicy == OobPolicy.ASK_UI_IF_OOB_NEEDED) {
                        ui.onRequireOobInput(new OobRequest(
                                caps.isStaticOOBInformationAvailable(),
                                caps.getInputOOBSize() > 0,
                                caps.getOutputOOBSize() > 0,
                                resolveInputActions(caps),
                                resolveOutputActions(caps),
                                caps.getInputOOBSize(),
                                caps.getOutputOOBSize()
                        ));
                    } else {
                        fail("OOB required but policy=FAIL_IF_OOB_NEEDED");
                    }
                }
            } catch (IllegalArgumentException ex) {
                fail(ex.getMessage() == null ? "IllegalArgumentException" : ex.getMessage());
            }
        });
    }

    private void setupProvisionerStateObservers() {
        vm.getProvisioningStatus().observe(owner, live -> {
            if (live == null) return;
            final ProvisionerProgress prog = live.getProvisionerProgress();
            if (prog == null) return;
            final ProvisionerStates state = prog.getState();
            ui.onProgressStateChanged(state);


            switch (state) {
                case PROVISIONING_FAILED: {
                    String msg = ProvisioningFailedState.parseProvisioningFailure(activity, prog.getStatusReceived());
                    fail(msg);
                    break;
                }
                case PROVISIONING_AUTHENTICATION_STATIC_OOB_WAITING:
                    if (options.staticOob != null) {
                        vm.getMeshManagerApi().setProvisioningAuthentication(options.staticOob);
                    } else {
                        requestOobFromUi(true, false, false);
                    }
                    break;
                case PROVISIONING_AUTHENTICATION_INPUT_OOB_WAITING:
                case PROVISIONING_AUTHENTICATION_OUTPUT_OOB_WAITING:
                    requestOobFromUi(false,
                            state == ProvisionerStates.PROVISIONING_AUTHENTICATION_INPUT_OOB_WAITING,
                            state == ProvisionerStates.PROVISIONING_AUTHENTICATION_OUTPUT_OOB_WAITING);
                    break;
                case APP_KEY_STATUS_RECEIVED:
                    // 配置完成点：触发收尾
                    setResultIntentIfComplete();
                    break;
                case COMPOSITION_DATA_STATUS_RECEIVED:
                case DEFAULT_TTL_STATUS_RECEIVED:
                case NETWORK_TRANSMIT_STATUS_RECEIVED:
                case PROVISIONING_COMPLETE:
                    // 任一关键步骤到达时均检查是否已满足全部完成条件
                    setResultIntentIfComplete();
                    break;
                case PROVISIONER_UNASSIGNED:
                    setResultIntentIfComplete();
                    break;
                default:
                    break;
            }
        });
    }

    private void requestOobFromUi(boolean isStatic, boolean isInput, boolean isOutput) {
        if (options.oobPolicy == OobPolicy.ASK_UI_IF_OOB_NEEDED) {
            final ProvisioningCapabilities caps = vm.getUnprovisionedMeshNode().getValue().getProvisioningCapabilities();
            ui.onRequireOobInput(new OobRequest(
                    isStatic,
                    isInput,
                    isOutput,
                    caps != null ? resolveInputActions(caps) : null,
                    caps != null ? resolveOutputActions(caps) : null,
                    caps != null ? caps.getInputOOBSize() : 0,
                    caps != null ? caps.getOutputOOBSize() : 0
            ));
        } else {
            fail("OOB required but no UI allowed by policy");
        }
    }

    // === 具体的启动分支 ===
    public void onPublicKeyAdded(@Nullable byte[] pub) {
        final UnprovisionedMeshNode node = vm.getUnprovisionedMeshNode().getValue();
        if (node == null) return;
        node.setProvisioneePublicKeyXY(pub);
        final ProvisioningCapabilities caps = node.getProvisioningCapabilities();
        if (caps != null && caps.getAvailableOOBTypes().size() == 1 &&
                caps.getAvailableOOBTypes().get(0) == AuthenticationOOBMethods.NO_OOB_AUTHENTICATION) {
            onNoOOBSelected();
        } else {
            // 需要 UI 选择 OOB 类型的情况，直接交给外层
            requestOobFromUi(false, caps != null && caps.getInputOOBSize() > 0, caps != null && caps.getOutputOOBSize() > 0);
        }
    }

    public void onNoOOBSelected() {
        final UnprovisionedMeshNode node = vm.getUnprovisionedMeshNode().getValue();
        if (node == null) return;
        try {
            if (options.nodeName != null) node.setNodeName(options.nodeName);
            setupProvisionerStateObservers();
            vm.getMeshManagerApi().startProvisioning(node);
        } catch (IllegalArgumentException ex) {
            fail(ex.getMessage() == null ? "Unknown error" : ex.getMessage());
        }
    }

    public void onStaticOOBSelected(@Nullable StaticOOBType unused) {
        startProvisionWithStaticOob();
    }

    public void onOutputOOBActionSelected(@NonNull OutputOOBAction action) {
        final UnprovisionedMeshNode node = vm.getUnprovisionedMeshNode().getValue();
        if (node == null) return;
        try {
            if (options.nodeName != null) node.setNodeName(options.nodeName);
            setupProvisionerStateObservers();
            vm.getMeshManagerApi().startProvisioningWithOutputOOB(node, action);
        } catch (IllegalArgumentException ex) {
            fail(ex.getMessage() == null ? "Unknown error" : ex.getMessage());
        }
    }

    public void onInputOOBActionSelected(@NonNull InputOOBAction action) {
        final UnprovisionedMeshNode node = vm.getUnprovisionedMeshNode().getValue();
        if (node == null) return;
        try {
            if (options.nodeName != null) node.setNodeName(options.nodeName);
            setupProvisionerStateObservers();
            vm.getMeshManagerApi().startProvisioningWithInputOOB(node, action);
        } catch (IllegalArgumentException ex) {
            fail(ex.getMessage() == null ? "Unknown error" : ex.getMessage());
        }
    }

    private void startProvisionWithStaticOob() {
        final UnprovisionedMeshNode node = vm.getUnprovisionedMeshNode().getValue();
        if (node == null) return;
        try {
            if (options.nodeName != null) node.setNodeName(options.nodeName);
            setupProvisionerStateObservers();
            vm.getMeshManagerApi().startProvisioningWithStaticOOB(node);
        } catch (IllegalArgumentException ex) {
            fail(ex.getMessage() == null ? "Unknown error" : ex.getMessage());
        }
    }

    // === 兜底：确保 MeshNetwork / Provisioner 可用，防止 NPE ===
    private void ensureNetworkAndProvisionerReady() {
        final no.nordicsemi.android.mesh.MeshManagerApi api = vm.getMeshManagerApi();
        MeshNetwork network = api.getMeshNetwork();
        if (network == null) {
            api.createMeshNetwork();
            network = api.getMeshNetwork();
        }
        if (network == null) return;
        Provisioner selected = network.getSelectedProvisioner();
        if (selected == null) {
            if (!network.getProvisioners().isEmpty()) {
                network.selectProvisioner(network.getProvisioners().get(0));
            } else {
                api.createMeshNetwork();
            }
        }
    }

    // === 完成/失败收尾 ===
    private void setResultIntentIfComplete() {
        final Intent ret = new Intent();
        final ProvisionerProgress progress = vm.getProvisioningStatus().getProvisionerProgress();

        // 特例：PROVISIONER_UNASSIGNED（无法继续配置），直接完成以便外层提示
        if (progress != null && progress.getState() == ProvisionerStates.PROVISIONER_UNASSIGNED) {
            ret.putExtra(Utils.PROVISIONING_COMPLETED, vm.isProvisioningComplete());
            ret.putExtra(Utils.PROVISIONER_UNASSIGNED, true);
            ui.onCompleted(ret);
            isRunning = false;
            return;
        }

        // 正常完成条件：完成配网 + 关键配置步骤完成
        final boolean provDone = vm.isProvisioningComplete();
        final boolean compDone = vm.isCompositionDataStatusReceived();
        final boolean ttlDone = vm.isDefaultTtlReceived();
        final boolean netTxDone = vm.isNetworkRetransmitSetCompleted();
        final MeshNetwork net = vm.getNetworkLiveData().getMeshNetwork();
        final boolean needAppKey = net != null && !net.getAppKeys().isEmpty();
        final boolean appKeyDone = vm.isAppKeyAddCompleted() || !needAppKey;
        final boolean configDone = compDone && ttlDone && netTxDone && appKeyDone;

        if (provDone && configDone) {
            ret.putExtra(Utils.PROVISIONING_COMPLETED, true);
            if (compDone) ret.putExtra(Utils.COMPOSITION_DATA_COMPLETED, true);
            if (ttlDone) ret.putExtra(Utils.DEFAULT_GET_COMPLETED, true);
            if (netTxDone) ret.putExtra(Utils.NETWORK_TRANSMIT_SET_COMPLETED, true);
            if (appKeyDone) ret.putExtra(Utils.APP_KEY_ADD_COMPLETED, true);

            try { vm.getNrfMeshRepository().setAutoReconnectEnabled(true); } catch (Exception ignored) {}
            try { vm.getNrfMeshRepository().setPreferProxyAnyNode(true); } catch (Exception ignored) {}
            try { mDelayHandler.removeCallbacksAndMessages(null); } catch (Exception ignored) {}
            ui.onCompleted(ret);
            isRunning = false;
        }
        // 未满足完成条件则不回调，等待后续状态
    }

    private void fail(@NonNull String reason) {
        try { vm.getNrfMeshRepository().setAutoReconnectEnabled(true); } catch (Exception ignored) {}
        try { vm.getNrfMeshRepository().setPreferProxyAnyNode(true); } catch (Exception ignored) {}
        try { vm.disconnect(); } catch (Exception ignored) {}
        try { mDelayHandler.removeCallbacksAndMessages(null); } catch (Exception ignored) {}
        ui.onFailed(reason);
        isRunning = false;
    }

    @Nullable
    private InputOOBAction[] resolveInputActions(ProvisioningCapabilities caps) {
        // 尝试新库方法：getInputOOBActions()
        try {
            Method m = caps.getClass().getMethod("getInputOOBActions");
            Object r = m.invoke(caps);
            if (r instanceof InputOOBAction[]) return (InputOOBAction[]) r;
        } catch (Throwable ignore) {}

        return null;
    }

    @Nullable
    private OutputOOBAction[] resolveOutputActions(ProvisioningCapabilities caps) {
        // 尝试新库方法：getOutputOOBActions()
        try {
            Method m = caps.getClass().getMethod("getOutputOOBActions");
            Object r = m.invoke(caps);
            if (r instanceof OutputOOBAction[]) return (OutputOOBAction[]) r;
        } catch (Throwable ignore) {}

        return null;
    }
}